{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\ruanjiananzhuang\\miniconda3\\envs\\langchain_rag\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from .autonotebook import tqdm as notebook_tqdm\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "# model = ChatOpenAI(model=\"gpt-4\")\n",
    "model = ChatOpenAI(\n",
    "  base_url=os.getenv(\"OPENAI_API_BASE_URL\"),  # deepseek的 API 地址\n",
    "  api_key=os.getenv(\"OPENAI_API_KEY\"),\n",
    "  model=\"deepseek-reasoner\",                          # 使用的模型名称\n",
    "  temperature=0.7\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "model.invoke([HumanMessage(content=\"Hi! I'm Bob\")])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.invoke([HumanMessage(content=\"What's my name?\")])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import AIMessage\n",
    "\n",
    "model.invoke(\n",
    "  [\n",
    "    HumanMessage(content=\"Hi! I'm Bob\"),\n",
    "    AIMessage(content=\"Hello Bob! How can I assist you today?\"),\n",
    "    HumanMessage(content=\"What's my name?\"),\n",
    "  ]\n",
    ")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 消息历史"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.chat_history import (\n",
    "  BaseChatMessageHistory,\n",
    "  InMemoryChatMessageHistory,\n",
    ")\n",
    "from langchain_core.runnables.history import RunnableWithMessageHistory\n",
    "\n",
    "store = {}\n",
    "\n",
    "\n",
    "def get_session_history(session_id: str) -> BaseChatMessageHistory:\n",
    "  if session_id not in store:\n",
    "    store[session_id] = InMemoryChatMessageHistory()\n",
    "  return store[session_id]\n",
    "\n",
    "\n",
    "with_message_history = RunnableWithMessageHistory(model, get_session_history)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "config = {\"configurable\": {\"session_id\": \"abc2\"}}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hi Bob! Nice to meet you. How can I help you today? 😊'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = with_message_history.invoke(\n",
    "  [HumanMessage(content=\"Hi! I'm Bob\")],\n",
    "  config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Your name is Bob! 😊 Nice to meet you!'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = with_message_history.invoke(\n",
    "  [HumanMessage(content=\"What's my name?\")],\n",
    "  config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 如果我们更改配置以引用不同的 session_id，我们可以看到它开始新的对话。\n",
    "config = {\"configurable\": {\"session_id\": \"abc3\"}}\n",
    "\n",
    "response = with_message_history.invoke(\n",
    "  [HumanMessage(content=\"What's my name?\")],\n",
    "  config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#然而，我们始终可以回到原始对话（因为我们将其保存在数据库中）\n",
    "\n",
    "config = {\"configurable\": {\"session_id\": \"abc2\"}}\n",
    "\n",
    "response = with_message_history.invoke(\n",
    "  [HumanMessage(content=\"What's my name?\")],\n",
    "  config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 提示词模板\n",
    "# 提示词模板帮助将原始用户信息转换为大型语言模型可以处理的格式。\n",
    "\n",
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "  [\n",
    "    (\n",
    "      \"system\",\n",
    "      \"You are a helpful assistant. Answer all questions to the best of your ability.\",\n",
    "    ),\n",
    "    MessagesPlaceholder(variable_name=\"messages\"),\n",
    "  ]\n",
    ")\n",
    "\n",
    "chain = prompt | model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hi Bob! Nice to meet you. How can I help you today? 😊'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"messages\": [HumanMessage(content=\"hi! I'm bob\")]})\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "with_message_history = RunnableWithMessageHistory(chain, get_session_history)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Hi Jim! Nice to meet you too! 😊  \\n(Wait, weren't you Bob a moment ago? 😄 Did you change your name or is this a secret identity?)\\n\\nEither way, how can I help you today, Jim?\""
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "config = {\"configurable\": {\"session_id\": \"abc5\"}}\n",
    "\n",
    "# 跟part2 是2种写法  这里是 Copilot 自动提示，Tab键补全的，仅仅看写法\n",
    "# response = with_message_history.invoke({\"messages\": [HumanMessage(content=\"hi! I'm bob\")]}, config=config)\n",
    "# response.content\n",
    "\n",
    "\n",
    "# 跟part2\n",
    "response = with_message_history.invoke(\n",
    "    [HumanMessage(content=\"Hi! I'm Jim\")],\n",
    "    config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Well, based on our conversation, you first introduced yourself as **Bob**, and then as **Jim**. 😄\\n\\nSo right now, I'll call you **Jim** — unless you'd like me to call you something else! Just let me know.\""
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = with_message_history.invoke(\n",
    "    [HumanMessage(content=\"What's my name?\")],\n",
    "    config=config,\n",
    ")\n",
    "\n",
    "response.content\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 提示模版加入变量: language\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You are a helpful assistant. Answer all questions to the best of your ability in {language}.\",\n",
    "        ),\n",
    "        MessagesPlaceholder(variable_name=\"messages\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "chain = prompt | model\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'¡Hola Bob! Un gusto conocerte. ¿En qué puedo ayudarte hoy?'"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 请注意，我们在提示中添加了一个新的 language 输入。我们现在可以调用链并传入我们选择的语言。\n",
    "response = chain.invoke(\n",
    "    {\"messages\": [HumanMessage(content=\"hi! I'm bob\")], \"language\": \"Spanish\"}\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 现在让我们将这个更复杂的链封装在一个消息历史类中。\n",
    "\n",
    "with_message_history = RunnableWithMessageHistory(\n",
    "    chain,\n",
    "    get_session_history,\n",
    "    input_messages_key=\"messages\",\n",
    ")\n",
    "\n",
    "config = {\"configurable\": {\"session_id\": \"abc11\"}}\n",
    "\n",
    "response = with_message_history.invoke(\n",
    "    {\"messages\": [HumanMessage(content=\"hi! I'm todd\")], \"language\": \"Spanish\"},\n",
    "    config=config,\n",
    ")\n",
    "\n",
    "response.content\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = with_message_history.invoke(\n",
    "    {\"messages\": [HumanMessage(content=\"whats my name?\")], \"language\": \"Spanish\"},\n",
    "    config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 管理对话历史"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.messages import SystemMessage, trim_messages\n",
    "\n",
    "trimmer = trim_messages(\n",
    "    max_tokens=65,\n",
    "    strategy=\"last\",\n",
    "    token_counter=model,\n",
    "    include_system=True,\n",
    "    allow_partial=False,\n",
    "    start_on=\"human\",\n",
    ")\n",
    "\n",
    "messages = [\n",
    "    SystemMessage(content=\"you're a good assistant\"),\n",
    "    HumanMessage(content=\"hi! I'm bob\"),\n",
    "    AIMessage(content=\"hi!\"),\n",
    "    HumanMessage(content=\"I like vanilla ice cream\"),\n",
    "    AIMessage(content=\"nice\"),\n",
    "    HumanMessage(content=\"whats 2 + 2\"),\n",
    "    AIMessage(content=\"4\"),\n",
    "    HumanMessage(content=\"thanks\"),\n",
    "    AIMessage(content=\"no problem!\"),\n",
    "    HumanMessage(content=\"having fun?\"),\n",
    "    AIMessage(content=\"yes!\"),\n",
    "]\n",
    "\n",
    "trimmer.invoke(messages)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "[SystemMessage(content=\"you're a good assistant\"),\n",
    " HumanMessage(content='whats 2 + 2'),\n",
    " AIMessage(content='4'),\n",
    " HumanMessage(content='thanks'),\n",
    " AIMessage(content='no problem!'),\n",
    " HumanMessage(content='having fun?'),\n",
    " AIMessage(content='yes!')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 要在我们的链中使用它，我们只需在将 messages 输入传递给提示之前运行修剪器。\n",
    "# 也就是修改链\n",
    "\n",
    "\n",
    "from operator import itemgetter\n",
    "\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "chain = (\n",
    "    RunnablePassthrough.assign(messages=itemgetter(\"messages\") | trimmer)\n",
    "    | prompt\n",
    "    | model\n",
    ")\n",
    "\n",
    "response = chain.invoke(\n",
    "    {\n",
    "        \"messages\": messages + [HumanMessage(content=\"what's my name?\")],\n",
    "        \"language\": \"English\",\n",
    "    }\n",
    ")\n",
    "response.content\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 询问最近的几条消息， llm 能正常回答\n",
    "\n",
    "response = chain.invoke(\n",
    "    {\n",
    "        \"messages\": messages + [HumanMessage(content=\"what math problem did i ask\")],\n",
    "        \"language\": \"English\",\n",
    "    }\n",
    ")\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 现在让我们将其包装在消息历史中\n",
    "\n",
    "with_message_history = RunnableWithMessageHistory(\n",
    "    chain,\n",
    "    get_session_history,\n",
    "    input_messages_key=\"messages\",\n",
    ")\n",
    "\n",
    "config = {\"configurable\": {\"session_id\": \"abc20\"}}\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = with_message_history.invoke(\n",
    "    {\n",
    "        \"messages\": messages + [HumanMessage(content=\"whats my name?\")],\n",
    "        \"language\": \"English\",\n",
    "    },\n",
    "    config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = with_message_history.invoke(\n",
    "    {\n",
    "        \"messages\": [HumanMessage(content=\"what math problem did i ask?\")],\n",
    "        \"language\": \"English\",\n",
    "    },\n",
    "    config=config,\n",
    ")\n",
    "\n",
    "response.content"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 流式处理\n",
    "\n",
    "config = {\"configurable\": {\"session_id\": \"abc15\"}}\n",
    "for r in with_message_history.stream(\n",
    "    {\n",
    "        \"messages\": [HumanMessage(content=\"hi! I'm todd. tell me a joke\")],\n",
    "        \"language\": \"English\",\n",
    "    },\n",
    "    config=config,\n",
    "):\n",
    "    print(r.content, end=\"|\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain_rag",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
